import * as step from '@ali/flow-step-toolkit'
import process from "process";
import fs from "fs";
import * as sinon from 'sinon';
import {expect} from 'chai'
import {afterEach, beforeEach} from 'mocha'
// @ts-ignore
import * as install from "../src/install";

let sandbox = sinon.createSandbox()
let exportEnvVar: sinon.SinonStub;
let addPath: sinon.SinonStub;
let stepInstall: sinon.SinonStub;
let rmSync: sinon.SinonStub;
let symlinkSync: sinon.SinonStub;

beforeEach(() => {
    sandbox.stub(process, 'env').value({
        ...process.env,
        FLOW_TOOL_REGISTRY_URL: 'https://tool-registry-url.com',
        FLOW_RUNNER_TOOL_CACHE_DIR: '/tmp/flow_tools'
    })

    exportEnvVar = sandbox.stub(step, 'exportEnvVar')
    addPath = sandbox.stub(step, 'addPath')
    stepInstall = sandbox.stub(step.tool, 'install')

    rmSync = sandbox.stub(fs, 'rmSync')
    symlinkSync = sandbox.stub(fs, 'symlinkSync')
})

afterEach(() => {
    sandbox.restore()
})

describe('install map jdk/maven version', () => {
    it('should return "" when input is empty or not support version', () => {
        expect(install.mapJDKVersion("")).to.equal("")
        expect(install.mapJDKVersion("jdk1.5")).to.equal("")
    })

    it('should return correct version', () => {
        expect(install.mapJDKVersion("jdk1.6")).to.equal("6")
        expect(install.mapJDKVersion("jdk1.7")).to.equal("7")
        expect(install.mapJDKVersion("jdk1.8")).to.equal("8")
        expect(install.mapJDKVersion("jdk1.9")).to.equal("9")
        expect(install.mapJDKVersion("jdk10")).to.equal("10")
        expect(install.mapJDKVersion("jdk11")).to.equal("11")
        expect(install.mapJDKVersion("jdk11.0.13")).to.equal("11")
        expect(install.mapJDKVersion("jdk17")).to.equal("17")
        expect(install.mapJDKVersion("jdk18")).to.equal("18")
        expect(install.mapJDKVersion("jdk19")).to.equal("19.0.1")
        expect(install.mapJDKVersion("jdk19.0.1")).to.equal("19.0.1")
        expect(install.mapJDKVersion("jdk20")).to.equal("20.0.2")
        expect(install.mapJDKVersion("jdk20.0.2")).to.equal("20.0.2")
        expect(install.mapJDKVersion("jdk21")).to.equal("21")
    })
})

describe('setup java/maven env and path', () => {
    it('when setup java env and path', () => {
        let jdkPath = "/srv/jdk19.0.1";
        install.setupJava(jdkPath)

        if (step.platform.getPlatform() === step.platform.PLATFORM_DARWIN) {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', `${jdkPath}/Contents/Home`)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkPath}/Contents/Home/bin`)
        } else {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', `${jdkPath}`)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkPath}/bin`)
        }
    })

    it('when setup maven env and path', () => {
        let mavenPath = "/srv/apache-maven-3.5.2"
        install.setupMaven(mavenPath)

        sinon.assert.calledOnceWithExactly(exportEnvVar, 'MAVEN_HOME', `${mavenPath}`)
        sinon.assert.calledOnceWithExactly(addPath, `${mavenPath}/bin`)
    })
})

describe('symlinkInstallPathToCachePath', () => {
    it('when installPath is undefined', () => {
        const jdkCachedPath: string = "/tmp/flow_tools"
        install.symlinkInstallPathToCachePath(undefined, jdkCachedPath)
        sinon.assert.notCalled(rmSync)
        sinon.assert.notCalled(symlinkSync)
    })

    it('when installPath is not undefined', () => {
        const jdkCachedPath: string = "/tmp/flow_tools"
        const installPath: string = "/srv/jdk19.0.1"
        install.symlinkInstallPathToCachePath(installPath, jdkCachedPath)
        sinon.assert.calledOnceWithExactly(rmSync, installPath, {recursive: true, force: true})
        sinon.assert.calledOnceWithMatch(symlinkSync, jdkCachedPath, installPath, 'dir')
    })
})

describe('install jdk', () => {
    it('install jdk when input jdk version is not support', async () => {
        let inputJdkVersion = 'jdk1.6';
        let promise: Promise<void> = install.installJDK(inputJdkVersion, undefined)
        let hasErr: boolean = false
        promise.then(() => {
            expect(true).to.be.false
        }).catch((err: Error) => {
            hasErr = true
            expect(err.message).to.equal(`JDK version ${inputJdkVersion} not support now.`)
        }).finally(() => {
            expect(hasErr).to.be.true
        })
    })

    it('Windows/macos does not support setup for JDK versions less than 11', async () => {
        let inputJdkVersion = 'jdk1.8'
        let promise: Promise<void> = install.installJDK(inputJdkVersion, undefined)
        let hasErr: boolean = false
        sandbox.stub(step.platform, 'getPlatform').returns('win')
        promise.then(() => {
            expect(true).to.be.false
        }).catch((err: Error) => {
            hasErr = true
            expect(err.message).to.equal(`Windows does not support setup for JDK versions: 6、7、8、9、10`)
        }).finally(() => {
            expect(hasErr).to.be.true
        })
    })

    const jdkCacheDir = "/tmp/flow_tools/jdk/19.0.1/x64";
    it('install jdk when input jdk version is support without cachedVersion and installPath is undefined', async () => {
        stepInstall.returns(jdkCacheDir)

        let inputJdkVersion = 'jdk19.0.1';
        await install.installJDK(inputJdkVersion, undefined)

        if (step.platform.getPlatform() === step.platform.PLATFORM_DARWIN) {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', `${jdkCacheDir}/Contents/Home`)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkCacheDir}/Contents/Home/bin`)
        } else {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', jdkCacheDir)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkCacheDir}/bin`)
        }
    })

    it('install jdk when input jdk version is support without cachedVersion and installPath is not undefined', async () => {
        stepInstall.returns(jdkCacheDir)

        let inputJdkVersion = 'jdk19.0.1';
        let installPath = '/srv/jdk19.0.1';
        await install.installJDK(inputJdkVersion, installPath)
        if (step.platform.getPlatform() === step.platform.PLATFORM_DARWIN) {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', `${jdkCacheDir}/Contents/Home`)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkCacheDir}/Contents/Home/bin`)
        } else {
            sinon.assert.calledOnceWithExactly(exportEnvVar, 'JAVA_HOME', jdkCacheDir)
            sinon.assert.calledOnceWithExactly(addPath, `${jdkCacheDir}/bin`)
        }

        sinon.assert.calledOnceWithExactly(rmSync, installPath, {recursive: true, force: true})
        sinon.assert.calledOnceWithMatch(symlinkSync, jdkCacheDir, installPath, 'dir')
    })
})

describe('install maven', () => {
    it('install maven when input maven version is not support', async () => {
        let inputMavenVersion = '2.2.0'
        let promise: Promise<void> = install.installJDK(inputMavenVersion, undefined)
        let hasErr: boolean = false
        promise.then(() => {
            expect(true).to.be.false
        }).catch((err: Error) => {
            hasErr = true
            expect(err.message).to.equal(`Maven version ${inputMavenVersion} not support now.`)
        }).finally(() => {
            expect(hasErr).to.be.true
        })
    })

    const mavenCacheDir = "/tmp/flow_tools/maven/3.5.2/x64";
    it('install maven when input maven version is support without cachedVersion and installPath is undefined', async () => {
        stepInstall.returns(mavenCacheDir)

        let inputMavenVersion = '3.5.2'
        await install.installMaven(inputMavenVersion, undefined)

        sinon.assert.calledOnceWithExactly(exportEnvVar, 'MAVEN_HOME', mavenCacheDir)
        sinon.assert.calledOnceWithExactly(addPath, `${mavenCacheDir}/bin`)
    })

    it('install maven when input maven version is support without cachedVersion and installPath is not undefined', async () => {
        stepInstall.returns(mavenCacheDir)

        let inputMavenVersion = '3.5.2';
        let installPath = '/srv/apache-maven-3.5.2';
        await install.installMaven(inputMavenVersion, installPath)
        sinon.assert.calledOnceWithExactly(exportEnvVar, 'MAVEN_HOME', mavenCacheDir)
        sinon.assert.calledOnceWithExactly(addPath, `${mavenCacheDir}/bin`)

        sinon.assert.calledOnceWithExactly(rmSync, installPath, {recursive: true, force: true})
        sinon.assert.calledOnceWithMatch(symlinkSync, mavenCacheDir, installPath, 'dir')
    })
})

afterEach(() => {
    process.env.FLOW_RUNNER_TOOL_CACHE_DIR = ''
})